#include"visibility_tester.h"
#include"../utils/m_libs.h"
#include"../objects/object.h"
#include"../media/medium.h"

VisibilityTester::VisibilityTester() :medium(NULL) {}
inline void VisibilityTester::setStartPt(P3 x) { startPt = x; }
inline void VisibilityTester::setTargetPt(P3 x) { targetPt = x; }
inline void VisibilityTester::setMedium(P3 &start_isect_n, Medium* outMedium, Medium* inMedium)
{
	medium = start_isect_n.dot(targetPt - startPt) < 0 ? outMedium : inMedium;
}
bool VisibilityTester::unoccluded(Scene* scene, Object* target)
{
	SurfaceInteraction si;
	Float t;
	Ray ray(startPt, (targetPt - startPt).norm());
	scene->nearest_intersect(ray, &t, &si);
	return static_cast<Object*>(si.pObj) == target;
}
Spectrum VisibilityTester::Tr(Scene* scene, Object* target, RandGenerator *randGen)
{
	Spectrum attenuation(1.f);
	Float t;
	Float tmax = (targetPt - startPt).len();
	Ray ray(startPt, (targetPt - startPt) / tmax, medium);
	//Medium* nextMedium = NULL;
	SurfaceInteraction si;
	while (true)
	{
		bool hit = scene->nearest_intersect(ray, &t, &si);
		Object* pObj = static_cast<Object*>(si.pObj);
		if (hit && pObj != target &&pObj->material != NULL)
		{
			//return black if occluded by an opaque object
			return Spectrum(0.f);
		}
		//occluded by unopaque object
		if (ray.medium)
		{
			Float rand = randGen->genRand();
			attenuation *= ray.medium->computeAttenuation(ray, t, &rand);
		}

		if (!hit||pObj==target)
		{
			break; //beam transmission computation completed.
		}
		//ray.o = t < eps ? ray.get(eps) : ray.get(t);
		ray.o = ray.get(t+eps);
		//tmax -= t;
		ray.medium = pObj->mi.getMedium(si.n, ray.d);
		//if (norm.dot(ray.d) < 0)
		//	ray.medium = nextMedium;

	}
	return attenuation;

}